package com.arc.level;

import arc.ArcServer;
import com.arc.message.SerializableVector;
import com.arc.state.AbstractState;
import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 *
 * @author Adam
 */
public abstract class AbstractLevelState extends AbstractAppState {
    
    public enum Type 
    {
        Client,
        Server
    };
    
    protected final static int snapshotSize = 3;
    protected Node levelRoot;
    protected AbstractState state;
    protected ArrayList<Snapshot> snapshotHistory;
    protected SimpleApplication app;
    protected Type  type;
    
    public AbstractLevelState(Type type, int snapshotSize)
    {
        this.type = type;
        levelRoot = new Node("levelRoot");  
        
        snapshotHistory = new ArrayList<Snapshot>();
    }
    
    @Override
    public void initialize(AppStateManager stateManager, Application app)
    {
        super.initialize(stateManager, app);
        
        this.app = (SimpleApplication) app;
        this.app.getRootNode().attachChild(levelRoot);
    }
    
    @Override
    public void update(float tpf)
    {
        
    }
    
   /* abstract public void processChanges();
    //Apply changes to scene graph
    //This should be abstract as it is different for client and server
    public void processChange(final Snapshot change) {
        
        //Parse the parent tree of the node in the change that we want to process.
        //This will give us some array like {root, ob1, ob1's child}
        String[] parentTree = change.getParentTree().split("-");
        final Spatial spat;
        Node parent = levelRoot;
        
        //Iterate through each parent node of our current node. During this we want to
        //  Check if the node exists: 
        //          -if it does then set it to our current parent node.
        //          -if not, create it and set this new node as the parent node.
                
        for(int i=0; i < parentTree.length; i++)
        {                
            //The current name in the tree is already selected as our parent.
            if(parent.getName() == parentTree[i])
                continue;
            
            //If the current name in the tree does not exist as a child in tree, create it and set it as parent.
            if(parent.getChild(parentTree[i]) == null)
            {
                Node newNode = new Node(parentTree[i]);
                parent.attachChild(newNode);
                parent = newNode;
                continue;
            }else{
                parent = (Node)parent.getChild(parentTree[i]);
            }
        }        
        
        //Now that we have selected the parent in a way in which we can ensure it's tree actually exists, we can update the spatial.
        if(parent.getChild(change.spatialName) == null){
            final Node finalCheck = parent;
            Box test = new Box(1f, 1f, 1f);
            Material tMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
            tMat.setColor("Color", ColorRGBA.Blue);
            Geometry geom = new Geometry(change.spatialName, test);
            geom.setUserData("classType", change.classType);
            geom.setLocalTranslation(change.translation.toVector3f());
            geom.setMaterial(tMat);
            finalCheck.attachChild(geom);
        }else{
            Spatial node = parent.getChild(change.spatialName);
            node.setLocalTranslation(change.getTranslation().toVector3f());
        }
        
    }
    //Request a change to the scene graph, return true if the change was submitted
    abstract public boolean submitChange(Snapshot change);
    */
    //This function pushes a snapshot into memoryit will put them in order starting with index 0 then 1 etc. 
    public void pushSnapshot(Snapshot newSnap)
    {
        snapshotHistory.add(newSnap);
    }
    
    
    //This creates a complete snapshot of the entire scene graph for an initializing player.
    public NetworkedNode generateNetworkedSceneGraph(Spatial currentSceneNode, NetworkedNode currentNetworkedNode)
    {
        if(currentSceneNode == null)
            return currentNetworkedNode;

        if(currentSceneNode instanceof Node){
            NetworkedNode node = new NetworkedNode((Node)currentSceneNode);

            if(currentNetworkedNode == null){
                currentNetworkedNode = node;
            }else{
                currentNetworkedNode.attachChild(node);
                currentNetworkedNode = node;
            }
            
            for(Spatial child : ((Node)currentSceneNode).getChildren()){
                generateNetworkedSceneGraph(child, currentNetworkedNode);
            }   
        }else if(currentSceneNode instanceof Geometry){
            NetworkedGeometry geom = NetworkedGeometryFactory.generateFromGeometry((Geometry)currentSceneNode);
            currentNetworkedNode.attachChild(geom);
        }    
        
        return currentNetworkedNode;
    }
    
    public Snapshot getSnapshot(int index)
    {
        if(index >= snapshotHistory.size() || index < 0)
            return null;
        
        return snapshotHistory.get(index);
    }
    
    public boolean indexInBounds(int index)
    {
        return (index < 0 || index >= snapshotSize);
    }
    
    public Node getRoot()
    { return levelRoot; }    
    
}
